package com.gjp.etfweb.abstracts;

import com.gjp.etfweb.bean.FundDetails;
import com.gjp.etfweb.bean.FundHistory;
import com.gjp.etfweb.common.Constant;
import com.gjp.etfweb.form.QueryForm;
import com.gjp.etfweb.utils.CommonUtils;
import com.gjp.etfweb.utils.GsonUtils;
import com.gjp.etfweb.utils.HttpsUtils;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 基金处理策略
 */
@Data
public abstract class AbstractPolicy {

    // 平均净值
    public BigDecimal averageCost;
    // 累计定投金额
    public BigDecimal totalBuyAmount;
    // 累计定投份额
    public BigDecimal totalShare;
    // 总投入天数
    public BigDecimal totalDay;
    // 定投收益
    public BigDecimal earningsAmount;
    // 定投收益率
    public BigDecimal earningsRate;
    // 年化收益率（投资内收益/本金）/（投资天数/365）
    public BigDecimal yearEarningsRate;
    // 累计间隔天数
    public BigDecimal totalDayInterval;
    // 总止盈次数
    public Integer stopNum;
    // 总成本
    public BigDecimal totalCost;
    // 总本金利润
    public BigDecimal totalProfit;
    // 定投份额
    public BigDecimal share;
    // 定投金额
    public BigDecimal buy;
    // 连续跌幅
    public BigDecimal continuousDecline;

    // 入参数据
    public QueryForm queryParam;

    /**
     * 初始化数据
     */
    public void init(QueryForm queryParam) {
        this.queryParam = new QueryForm();
        BeanUtils.copyProperties(queryParam, this.queryParam);
        averageCost = BigDecimal.ZERO;
        totalBuyAmount = BigDecimal.ZERO;
        totalShare = BigDecimal.ZERO;
        totalDay = BigDecimal.ZERO;
        earningsAmount = BigDecimal.ZERO;
        earningsRate = BigDecimal.ZERO;
        yearEarningsRate = BigDecimal.ZERO;
        stopNum = 0;
        totalCost = BigDecimal.ZERO;
        totalProfit = BigDecimal.ZERO;
        totalDayInterval = BigDecimal.ZERO;
        share = BigDecimal.ZERO;
        buy = BigDecimal.ZERO;
        continuousDecline = BigDecimal.ZERO;
    }

    /**
     * 解析对应基金的详细信息
     */
    public List<FundHistory> pullData(String fileRoot) {
        // 获取查询参数
        String code = queryParam.getCode();
        Boolean isNew = queryParam.getIsNew();
        String apiUrl = "https://api.doctorxiong.club/v1/fund/detail?code=" + code;
        File file = new File(fileRoot + File.separator + code + ".txt");
        if (isNew && file.exists()) {
            file.delete();
        }
        List<FundHistory> fundHistoryList = Lists.newArrayList();
        try {
            JsonObject data = new JsonObject();
            if (file.exists() && file.length() > 0) {
                String fundHistoryListJson = Files.readAllLines(file.toPath()).get(0);
                data = new Gson().fromJson(fundHistoryListJson, JsonObject.class);
            } else {
                if (!file.exists()) {
                    file.createNewFile();
                } else {
                    file.delete();
                }
                String responseData = HttpsUtils.get(apiUrl);
                if (!StringUtils.isEmpty(responseData)) {
                    JsonObject root = new Gson().fromJson(responseData, JsonObject.class);
                    data = root.get("data").getAsJsonObject();
                    // 保存到文件
                    Files.write(file.toPath(), GsonUtils.GSON.toJson(data).getBytes());
                }
            }
            String name = data.get("name").getAsString();
            JsonArray netWorthData = data.getAsJsonArray("netWorthData");
            for (JsonElement worthDatum : netWorthData) {
                JsonArray worthDatumArray = worthDatum.getAsJsonArray();
                //日期
                Date date = CommonUtils.parseDate(worthDatumArray.get(0).getAsString(), "yyyy-MM-dd");
                //净值
                BigDecimal netWorth = worthDatumArray.get(1).getAsBigDecimal();
                //涨幅
                BigDecimal increaseRate = worthDatumArray.get(2).getAsBigDecimal();
                //备注
                String remark = worthDatumArray.get(3).getAsString();
                FundHistory fundHistory = new FundHistory();
                fundHistory.setCode(code);
                fundHistory.setName(name);
                fundHistory.setDate(date);
                fundHistory.setIncreaseRate(increaseRate);
                fundHistory.setNetWorth(netWorth);
                fundHistoryList.add(fundHistory);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        fundHistoryList.sort(Comparator.comparing(FundHistory::getDate));
        return fundHistoryList;
    }


    /**
     * 检查止盈点
     *
     * @return
     */
    public boolean checkStopPoint() {
        if (totalShare.compareTo(BigDecimal.ZERO) > 0) {
            // 检查止盈点
            if (yearEarningsRate.compareTo(queryParam.getStopPoint()) >= 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 计算指标数据
     *
     * @param fundHistory
     */
    public void processRate(FundHistory fundHistory) {
        // 有持仓
        if (totalShare.compareTo(BigDecimal.ZERO) > 0) {
            // 1. 先算历史收益
            // 定投收益 (基金净值-平均成本)*累计定投份额 ，或总市值-累计定投金额。
            earningsAmount = fundHistory.getNetWorth()
                    .subtract(averageCost)
                    .multiply(totalShare);
            // 定投收益率 (定投收益/累计定投金额)
            if (totalBuyAmount.compareTo(BigDecimal.ZERO) > 0) {
                earningsRate = earningsAmount
                        .divide(totalBuyAmount, Constant.DECIMAL_POINT, BigDecimal.ROUND_HALF_UP);
            }
            // 年化收益率=（投资内收益/本金）/（投资天数/365）
            if (totalBuyAmount.compareTo(BigDecimal.ZERO) > 0 && totalDay.compareTo(BigDecimal.ZERO) > 0) {
                // 计算投资天数
                BigDecimal investmentDays = totalDay.divide(BigDecimal.valueOf(365), Constant.DECIMAL_POINT, BigDecimal.ROUND_HALF_UP);
                if (investmentDays.compareTo(BigDecimal.ONE) < 1) {
                    //最小等于1
                    investmentDays = BigDecimal.ONE;
                }
                yearEarningsRate = earningsAmount
                        .divide(totalBuyAmount, Constant.DECIMAL_POINT, BigDecimal.ROUND_HALF_UP)
                        .divide(investmentDays, Constant.DECIMAL_POINT, BigDecimal.ROUND_HALF_UP);
            }
        }
    }

    public void processBusiness(FundHistory fundHistory) {
        // 总定投天数 （当前时间减去入场时间）
        totalDay = totalDay.add(BigDecimal.ONE);
        // 初始值
        share = BigDecimal.ZERO;
        buy = BigDecimal.ZERO;
        // 卖出策略
        boolean isSell = sellPoint(fundHistory);
        // 只有没达到止盈点，才执行买入点
        if (!isSell) {
            // 执行买入策略
            Map<String, BigDecimal> buyAndShareMap = buyPoint(fundHistory);
            if (!CollectionUtils.isEmpty(buyAndShareMap)) {
                buy = buyAndShareMap.get("buy");
                share = buyAndShareMap.get("share");
            }
        }
        // 统计累计份额和累计金额
        totalShare = totalShare.add(share);
        totalBuyAmount = totalBuyAmount.add(buy);
        // 计算平均成本 (累计定投金额/累计定投份额)
        if (totalShare.compareTo(BigDecimal.ZERO) > 0) {
            averageCost = totalBuyAmount.divide(totalShare, Constant.DECIMAL_POINT, BigDecimal.ROUND_HALF_UP);
        }
    }

    /**
     * 卖出策略 默认清仓
     *
     * @param fundHistory
     * @return
     */
    public boolean sellPoint(FundHistory fundHistory) {
        // 判断是否达到止盈点，如果达到止盈点，则停止买入，开始止盈
        if (checkStopPoint()) {
            // 超出止盈点，全部卖出
            totalCost = totalCost.add(totalBuyAmount);
            totalProfit = totalProfit.add(totalShare.multiply(fundHistory.getNetWorth()));
            // 止盈次数+1
            stopNum++;
            // 清空相关数据
            clearData(fundHistory);
            return true;
        }
        return false;
    }

    public void clearData(FundHistory fundHistory) {
        // 重置开始时间
        queryParam.setStartTime(CommonUtils.dateToString(fundHistory.getDate()));
        // 清空总份额
        totalShare = BigDecimal.ZERO;
        totalBuyAmount = BigDecimal.ZERO;
        totalDay = BigDecimal.ZERO;
        averageCost = BigDecimal.ZERO;
        // 清空收益
        earningsRate = BigDecimal.ZERO;
        yearEarningsRate = BigDecimal.ZERO;
        earningsAmount = BigDecimal.ZERO;
    }

    public FundDetails getFundDetails(FundHistory fundHistory) {
        FundDetails fundDetails = new FundDetails();
        fundDetails.setDate(fundHistory.getDate());
        fundDetails.setNetWorth(fundHistory.getNetWorth());
        fundDetails.setIncreaseRate(fundHistory.getIncreaseRate());
        fundDetails.setTotalBuyAmount(BigDecimal.valueOf(totalBuyAmount.doubleValue()));
        fundDetails.setTotalDay(BigDecimal.valueOf(totalDay.doubleValue()));
        fundDetails.setTotalShare(BigDecimal.valueOf(totalShare.doubleValue()));
        fundDetails.setYearEarningsRate(BigDecimal.valueOf(yearEarningsRate.doubleValue()));
        fundDetails.setEarningsAmount(BigDecimal.valueOf(earningsAmount.doubleValue()));
        fundDetails.setEarningsRate(BigDecimal.valueOf(earningsRate.doubleValue()));
        fundDetails.setAverageCost(BigDecimal.valueOf(averageCost.doubleValue()));
        fundDetails.setBuyAmount(BigDecimal.valueOf(buy.doubleValue()));
        fundDetails.setShare(BigDecimal.valueOf(share.doubleValue()));
        return fundDetails;
    }

    /**
     * 买入策略
     *
     * @param fundHistory
     * @return 返回一个购买金额，购买分额的map
     */
    public abstract Map<String, BigDecimal> buyPoint(FundHistory fundHistory);


    public void printDetails(FundDetails fundDetails) {
        System.out.print("日期：");
        System.out.print(CommonUtils.dateFormat.format(fundDetails.getDate()));
        System.out.print("\t");
        System.out.print("基金净值：");
        System.out.print(CommonUtils.numberFormat.format(fundDetails.getNetWorth()));
        System.out.print("\t");
        System.out.print("定投金额：");
        System.out.print(fundDetails.getBuyAmount().stripTrailingZeros().toPlainString());
        System.out.print("\t");
        System.out.print("总定投金额：");
        System.out.print(fundDetails.getTotalBuyAmount().stripTrailingZeros().toPlainString());
        System.out.print("\t");
        System.out.print("总定投份额：");
        System.out.print(CommonUtils.numberFormat.format(fundDetails.getTotalShare()));
        System.out.print("\t");
        System.out.print("平均成本：");
        System.out.print(fundDetails.getAverageCost().stripTrailingZeros().toPlainString());
        System.out.print("\t");
        System.out.print("涨跌：");
        System.out.print(CommonUtils.percentFormat.format(fundDetails.getIncreaseRate().divide(BigDecimal.valueOf(100))));
        System.out.print("\t");
        System.out.print("定投收益率：");
        System.out.print(CommonUtils.percentFormat.format(fundDetails.getEarningsRate()));
        System.out.print("\t");
        System.out.print("年化收益率：");
        System.out.print(CommonUtils.percentFormat.format(fundDetails.getYearEarningsRate()));
        System.out.print("\t");
        System.out.print("定投收益：");
        System.out.print(CommonUtils.numberFormat.format(fundDetails.getEarningsAmount()));
        System.out.println();
    }
}
